<?php
/**
 * Created by PhpStorm.
 * User: zhengze
 * Date: 2019/7/1
 * Time: 18:29
 */

namespace app\admin\validate;

use app\common\model\Admin;
use app\common\model\AdminGroup;
use app\common\model\AdminPost;
use app\common\model\AdminStructure;
use app\api\enum\ScopeEnum;
use app\api\service\Token;
use common\cache\TheRedis;
use common\exception\AdminException;
use common\exception\ExceptionEmun;
use common\exception\GroupException;
use common\exception\PostException;
use common\exception\StructureException;
use common\exception\SystemException;
use common\helper\YaconfHelper;
use common\traits\InstanceTrait;
use common\validate\BaseValidate;
use app\lib\exception\TokenException;

/**
 * Class AdminValidate
 * 验证类的基类
 */
class AdminValidate extends BaseValidate
{
    use InstanceTrait;

    /**
     * @var Admin
     */
    protected $tempAdmin = null;
    protected $captcha_id = null;

    /**
     * 定义验证规则(公用)
     * 格式：'字段名|字段中文名'    =>    ['规则1','规则2'...]
     * @var array
     */
    protected $rule = [
        'id|参数' => ['require',],
        'ids|参数' => ['require',],
        'status|状态' => ['require',],
        'password|密码' => ['require', 'length:5,20'],
        'username|用户名' => ['require', 'length:2,12'],
        'old_pwd|旧密码' => ['require',],
        'new_pwd|新密码' => ['require',],
        'auth_key|token' => ['require',],
        'realname|真实姓名' => ['require',],
        'rememberKey|rememberKey' => ['require',],
        'groups|角色' => ['require', 'array'],
        'structure_id|部门' => ['require', 'integer'],
    ];

    protected $sceneRule = [
        'index' => [
            'only' => [''],
        ],
        'login' => [
            'only' => ['username', 'password', 'isRemember', 'captcha'],
            'append' => [
                'password' => 'checkPasswordRight',
            ],
        ],
        'relogin' => [
            'only' => ['rememberKey'],
        ],
        'read' => [
            'only' => ['id'],
        ],
        'delete' => [
            'only' => ['id'],
        ],
        'deletes' => [
            'only' => ['ids'],
        ],
        'enables' => [
            'only' => ['ids', 'status'],
        ],
        'setInfo' => [
            'only' => ['old_pwd', 'new_pwd', 'auth_key'],
            'append' => [
                'old_pwd' => ['checkPasswordRight', 'checkPasswordUnique'],
            ],
        ],
        'save' => [
            'only' => ['username', 'password', 'realname', 'groups', 'structure_id'],
            'append' => [
                'username' => 'checkNameUnique',
                'groups' => 'checkGroupsExist',
                'structure_id' => 'checkStructureExist',
            ],
        ],
        'update' => [
            'only' => ['username', 'realname','groups','structure_id'],
            'append' => [
                'username' => 'checkNameUnique',
                'groups' => 'checkGroupsExist',
                'structure_id' => 'checkStructureExist',
            ],
        ],
    ];

    /**
     * RunCheck前执行,此处用于验证码的校验
     * @param $data
     */
    protected function beforRunCheck($data)
    {
        if (parent::beforRunCheck($data)) {
            if (!empty(strtolower(request()->action()) == 'login')) {
                $data['captcha'] = $data['captcha'] ?? '';
                $this->checkCaptcha($data['captcha'], $data);
            }
            return true;
        }
        return false;
    }

    /**
     * 抛异常前执行
     * @param $data
     */
    protected function beforExcetion($data)
    {
        if (parent::beforExcetion($data)) {
            if (!empty(strtolower(request()->action()) == 'login')) {
                $this->incLoginTimes($_SERVER['REMOTE_ADDR']);
                $login_error_times = $this->getLoginTimes($_SERVER['REMOTE_ADDR']);
                if ($login_error_times >= YaconfHelper::get('login_max_times',5)) {
                    $this->exception_data['captcha'] = 1;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 检测验证码
     * @param $value
     * @return bool
     * @throws \Exception
     */
    protected function checkCaptcha($value, $data)
    {
        //错误次数达到设置最大次数
        $login_error_times = $this->getLoginTimes($_SERVER['REMOTE_ADDR']);
        $login_max_times = YaconfHelper::get('login_max_times',5);
        if ($login_error_times >= $login_max_times) {
            $this->error_field = 'captcha';
            //手动抛出的异常,不能用$this->exception_data
            if (empty($value)) {
                ExceptionEmun::throwException(SystemException::NO_CAPTCHA);
            }
            $id = str_replace('.', '', $_SERVER['REMOTE_ADDR']);
            $res = captcha_check($value, $id);
            if (false == $res) {
                ExceptionEmun::throwException(SystemException::CAPTCHA_ERROR);
            }
        }
        return true;
    }

    /**
     * 检测角色是否存在
     * @param $value
     * @return bool
     * @throws \Exception
     */
    protected function checkGroupsExist($value, $rule, $data = [])
    {
        $group_ids = AdminGroup::where('group_id', 'in', $value)->column('group_id');
        if (!empty($group_ids)) {
            if ($value == $group_ids) {
                return true;
            }
        }
        return ExceptionEmun::getExctprion(GroupException::NO_EXIST);
    }

    /**
     * 检测部门是否存在
     * @param $value
     * @return bool
     * @throws \Exception
     */
    protected function checkStructureExist($value, $rule, $data = [])
    {
        $structure_id = AdminStructure::where('structure_id', '=', $value)->column('structure_id');
        if (!empty($structure_id)) {
            return true;
        }
        return ExceptionEmun::getExctprion(StructureException::NO_EXIST);
    }

    /**
     * 检测岗位是否存在
     * @param $value
     * @return bool
     * @throws \Exception
     */
    protected function checkPostExist($value, $rule, $data = [])
    {
        $structure_id = AdminPost::where('post_id', '=', $value)->column('structure_id');
        if (!empty($structure_id) && $data['structure_id'] == $structure_id) {
            return true;
        }
        return ExceptionEmun::getExctprion(PostException::NO_EXIST);
    }

    /**
     * 检测名字重复
     * @param $value
     * @param $rule
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    protected function checkNameUnique($value, $rule, $data = [])
    {
        $Admin = Admin::instance();
        $this->tempAdmin = $Admin->getByField('username', $value);
        $pk = 'id';
        if (!empty($this->tempAdmin)) {
            if (!empty($data[$pk])) {
                //修改帐号
                if ($data[$pk] != $this->tempAdmin[$pk]) {
                    return ExceptionEmun::getExctprion(AdminException::NAME_REPEAT);
                }
            } else {
//                创建帐号
                return ExceptionEmun::getExctprion(AdminException::NAME_REPEAT);
            }
        }
        return true;
    }

    /**
     * 检测密码重复
     * @param $value
     * @param $rule
     * @param array $data
     * @return bool
     */
    protected function checkPasswordUnique($value, $rule, $data = [])
    {

        if ($data['new_pwd'] == $data['old_pwd']) {
            return ExceptionEmun::getExctprion(AdminException::PASSWORD_REPEAT);
        }

        return true;
    }


    /**
     * 检测密码是否正确
     * @param $value
     * @param $rule
     * @param array $data
     * @return bool
     * @throws \Exception
     */
    protected function checkPasswordRight($value, $rule, $data = [])
    {
        $this->initTempAdmin($data);
//      验证
        if (!empty($this->tempAdmin)) {
            if (password_verify($value, $this->tempAdmin['password'])) {
                return true;
            } else {
                return ExceptionEmun::getExctprion(AdminException::INCORRECT_PASSWORD);
            }
        } else {
            return ExceptionEmun::getExctprion(AdminException::NO_EXIST);
        }
    }

    /**
     * 登陆失败次数+1
     * @param $mark
     * @return bool
     */
    private function incLoginTimes($mark)
    {

        $key = TheRedis::instance()->generate_cache_key('admin', $mark, 'login_times');
        $login_times = cache($key);
        if (!empty($login_times)) {
            $login_times++;
        } else {
            $login_times = 1;
        }
        cache($key, $login_times, [
            'expire' => 60 * 15,
        ]);
        return true;
    }

    /**
     * 获取登陆失败次数
     * @param type $mark 唯一标识
     * @return integer
     */
    private function getLoginTimes($mark)
    {
        $key = TheRedis::instance()->generate_cache_key('admin', $mark, 'login_times');
        $login_times = cache($key);
        return $login_times ? $login_times : 0;
    }


    /**
     * @return mixed|null
     */
    public function getCaptchaId()
    {
        return $this->captcha_id;
    }

    public function __construct(array $rules = [], array $message = [], array $field = [])
    {
        parent::__construct($rules, $message, $field);
        $this->captcha_id = str_replace('.', '', $_SERVER['REMOTE_ADDR']);
    }

    /**
     * 初始化tempAdmin
     * @param array $data
     */
    public function initTempAdmin(array $data): void
    {
        if (!empty($this->tempAdmin)) {
            return;
        }

        $Admin = new Admin();
        $get_admin_field = ['username' => '', 'auth_key' => ''];
        if (array_intersect_key($get_admin_field, $data) == false) {
            //没有获取Admin信息的唯一标识
            ExceptionEmun::throwException(AdminException::NO_EXIST);
        }
        if (empty($this->tempAdmin) && !empty($data['username'])) {
            $this->tempAdmin = $Admin->getByName($data['username']);
        }
        if (empty($this->tempAdmin) && !empty($data['auth_key'])) {
            $this->tempAdmin = $Admin->getByToken($data['auth_key']);
        }
    }


    /**
     * 获取接口参数的临时帐号,只用于外部调用
     * @return null|Admin
     */
    public function getTempAdmin(): ?Admin
    {
        if (empty($this->tempAdmin)) {
            ExceptionEmun::throwException(AdminException::NO_EXIST);
        }

        return $this->tempAdmin;
    }

}